API for out-of-band scale-to-zero pin#237
Conversation
Adds two new endpoints to the kernel-images server: - POST /system/standby/disable — pins scale-to-zero off until released - POST /system/standby/enable — releases the pin The pin lives alongside the existing request-driven middleware refcount in DebouncedController: scale-to-zero stays disabled while either holders are inflight requests OR the pin is held. Request-driven Enable calls do not release the pin, so a pinned VM survives idle periods. Releasing the pin honors any configured re-enable cooldown. This is the in-VM surface for future control-plane integrations (e.g. a hot-pool controller reserving a VM until it is claimed). Control-plane wiring will follow in metro-api and the API server. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Spins up the headless image via testcontainers and exercises: - Idempotent disable (two consecutive 204s) - A normal request flows while pinned (middleware coexistence) - Idempotent enable (two consecutive 204s) The unikraft control file does not exist inside the docker test container, so the underlying scale-to-zero write is a no-op. The test validates HTTP wiring and handler/middleware coexistence; the deep pin semantics are covered by unit tests against DebouncedController. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Address review feedback:
- Path /system/standby/* implied VM-state mutation; rename to
/scaletozero/{pin,unpin} so the operation is specific to the
scale-to-zero gate.
- Interface methods DisablePin/EnablePin read as inverted; rename to
Pin/Unpin for clarity.
- Rewrite openapi summary/description to be caller-focused (what it
does, when to call, what pairs with).
Match user-facing terminology to the action ("disable scale to zero")
rather than the internal pin mechanism. Internal PinnedController.Pin/Unpin
methods retain pin/unpin naming since they're distinct from the refcounted
Controller.Disable/Enable.
Rename refcounted hold methods to Acquire/Release so that Disable/Enable
can carry the idempotent persistent-toggle semantics defined by the
/scaletozero/{disable,enable} API. Split the low-level direct toggle out
into a separate Toggler interface (unikraftCloudToggler) wrapped by
DebouncedController.
This reverts commit 7d26ce6.
PinnedController's Pin/Unpin are the in-scope additions for the
/scaletozero/{disable,enable} endpoints. Rename them to DisableStz/EnableStz
so the verb matches the API. The pre-existing refcounted Controller.Disable
/Enable is left untouched, since DisableStz/EnableStz avoids a method-name
collision on the concrete DebouncedController.
This reverts commit d6e82dd.
Previously, Unpin set c.pinned = false before calling maybeReenableLocked. If the underlying ctrl.Enable returned an error (no-cooldown path), the pin was already released but c.disabled remained true, so a retry of Unpin hit the !c.pinned early-return and became a no-op. The controller was stuck disabled with no API-driven recovery path. Restore c.pinned on error so the caller can retry, mirroring Pin's "flip the state flag only after the side effect succeeded" pattern.
Monitoring Plan:
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8361537. Configure here.
Sayan-
left a comment
There was a problem hiding this comment.
yeet - thanks for iterating!

Summary
Adds two new endpoints to the kernel-images server:
POST /system/standby/disable— pins scale-to-zero off until releasedPOST /system/standby/enable— releases the pinThe pin lives alongside the existing request-driven middleware refcount inside
DebouncedController:Enable(from middleware) does not release the pinDebouncedControllerandNoopControllernow also implement a newPinnedControllersub-interface (Controller+DisablePin/EnablePin). The pin is a boolean —DisablePin/EnablePinare idempotent.Why
This is the in-VM surface needed for a future control-plane integration: an external system (e.g. a hot-pool controller) needs to hold a VM out of standby while it sits idle in a pool, then release the hold when the VM is claimed.
The existing middleware refcount only works for inflight HTTP requests, so it can't hold a VM disabled across an idle period.
Notes for reviewers
NewDebouncedController*widened fromControllerto*DebouncedControllerso callers can access the pin methods. Only existing caller iscmd/api/main.go, which is unaffected since*DebouncedControllerstill satisfiesControllerforrecorder.NewFFmpegRecorderFactoryandscaletozero.Middleware.Test plan
go test -race ./lib/scaletozero/...passes (6 new tests covering pin semantics)go test -race ./cmd/api/...passesgo vet ./...cleango build ./...cleanlib/oapi/oapi.goNote
Medium Risk
Introduces new API endpoints and modifies
DebouncedControllerstate machine to include a pinned hold, which could affect VM standby/idle behavior and timing if the pin logic misbehaves.Overview
Adds control-plane style endpoints
POST /scaletozero/disableandPOST /scaletozero/enablethat pin scale-to-zero off/on independently of the existing per-request middleware refcount.Extends
scaletozerowith a newPinnedControllerinterface and updatesDebouncedControllerto track a booleanpinnedhold that blocks request-driven re-enables until explicitly unpinned (still honoring the existing cooldown behavior).Regenerates
openapi.yaml/lib/oapifor the new routes and adds unit + e2e coverage to validate idempotency and that the pin does not interfere with normal request handling.Reviewed by Cursor Bugbot for commit e15d112. Bugbot is set up for automated code reviews on this repo. Configure here.